import { createTree } from '@/utils';
import { ref } from 'vue';

interface SelectedOptions<D = any> {
  data?: D[];
  childrenKey?: string;
  id?: string;
  pid?: string;
  autoId?: boolean;
  disabled?: (item: D) => boolean;
  multiple?: boolean;
}

const defaultOptions = {
  childrenKey: 'children',
  id: 'id',
  pid: 'pid',
  autoId: true,
  disabled: () => false,
  multiple: true,
};

// 定义扩展后的类型
type ExtendedItem<D, K extends keyof any> = D & {
  [P in K]?: P extends '$_id'
  ? string
  : P extends '$_index'
  ? number
  : P extends '$_parentId'
  ? string
  : never;
};

export const useSelect = <D extends Record<string, any> = any>(
  options: SelectedOptions<D> = {}
) => {
  const mergeOptions = { ...defaultOptions, ...options };
  const originalData = ref<D[]>(mergeOptions.data || []);
  const selectedData = ref<D[]>([]);
  const ids = ref<Set<string | number>>(new Set());
  const selectIds = ref<(string | number)[]>([]);
  const flatData = ref<ExtendedItem<D, '$_id' | '$_index' | '$_parentId'>[]>([]);
  const newData = ref<D[]>([]);
  const currentSelected = ref<D>({} as D);
  const itemMap = new Map<string, ExtendedItem<D, '$_id' | '$_index' | '$_parentId'>>();

  const createFlatData = (
    data: D[],
    parentIds: (string | number)[] = []
  ): ExtendedItem<D, '$_id' | '$_index' | '$_parentId'>[] => {
    return data.flatMap((item: D, index: number) => {
      const currentParentIds = [...parentIds, mergeOptions.autoId ? index : item[mergeOptions.id]];
      const newItem: ExtendedItem<D, '$_id' | '$_index' | '$_parentId'> = {
        ...item,
        // $_index: data.length - 1,
        $_parentId: parentIds.join('-'),
        $_checked: false,
        $_disabled: () => mergeOptions.disabled?.(item),
      };

      newItem.$_id = currentParentIds.join('-');
      itemMap.set(currentParentIds.join('-'), newItem);

      const children = (item[mergeOptions.childrenKey as keyof D] as D[]) || [];
      if (children.length > 0) {
        return [newItem, ...createFlatData(children, currentParentIds)];
      }
      return [newItem];
    });
  };

  const setSelect = (data: D) => {
    if (mergeOptions.multiple) {
      currentSelected.value = {};
    } else {
      currentSelected.value = data;
      return;
    }
    const id = mergeOptions.autoId
      ? (data as ExtendedItem<D, '$_id'>).$_id
      : data[mergeOptions.id as keyof D];

    if (!id) return;

    if (ids.value.has(id)) {
      ids.value.delete(id);
      selectedData.value = selectedData.value.filter(
        (item) =>
          (mergeOptions.autoId ? (item as ExtendedItem<D, '$_id'>).$_id : item[mergeOptions.id]) !==
          id
      );
    } else {
      ids.value.add(id);
      // @ts-ignore
      selectedData.value.push(data);
    }
    selectIds.value = Array.from(ids.value);
  };

  const getParent = (data: D) => {
    return itemMap.get(data.$_parentId);
  };

  const getAllParents = (data: D) => {
    const parents = [{ ...data }];
    let parent = getParent(data);
    while (parent) {
      parents.unshift(parent);
      parent = getParent(parent);
    }
    return parents;
  };

  flatData.value = createFlatData(originalData.value as any);
  newData.value = createTree(flatData.value as any, undefined, (node, parent) => {
    return parent === undefined
      ? node.$_parentId === ''
      : parent && node.$_parentId === (parent as ExtendedItem<D, '$_id'>).$_id;
  }) as D[];
  const findItem = (cb: any): D => {
    if (typeof cb === 'function') {
      return cb(flatData.value)
    }
    return itemMap.get(cb as string) || {} as D;
  }
  return {
    setSelect,
    originalData,
    selectedData,
    flatData,
    data: newData,
    selectIds,
    currentSelected,
    getParent,
    getAllParents,
    findItem
  };
};
